// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package errors

import (
	"internal/reflectlite"
)

// Unwrap返回在err上调用Unwrap方法的结果，如果err为
// 类型包含返回错误的展开方法。
// 否则，“展开”将返回零。
func Unwrap(err error) error {
	u, ok := err.(interface {
		Unwrap() error
	})
	if !ok {
		return nil
	}
	return u.Unwrap()
}

// Is报告err链中的任何错误是否与目标匹配。
// None
// 该链由err本身和由err获得的错误序列组成
// 反复呼叫“展开”。
// None
// 如果某个错误等于某个目标，或者
// 它实现了一个方法Is（error）bool，使得Is（target）返回true。
// None
// 错误类型可能提供Is方法，因此可以将其视为等效的
// 更改为现有错误。例如，如果MyError定义
// None
// func（m MyError）是（目标错误）bool{return target==fs.ErrExist}
// None
// 然后Is（MyError{}，fs.ErrExist）返回true。有关详细信息，请参阅syscall.Errno.Is
// 标准库中的一个示例。
func Is(err, target error) bool {
	if target == nil {
		return err == target
	}

	isComparable := reflectlite.TypeOf(target).Comparable()
	for {
		if isComparable && err == target {
			return true
		}
		if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
			return true
		}
		// 待办事项：考虑支持目标。这将允许
		// 用户可定义的谓词，但也允许处理草率的
		// API，从而更容易使用它们。
		if err = Unwrap(err); err == nil {
			return false
		}
	}
}

// As查找err链中与目标匹配的第一个错误，如果是，则设置
// 目标设置为该错误值并返回true。否则，它将返回false。
// None
// 该链由err本身和由err获得的错误序列组成
// 反复呼叫“展开”。
// None
// 如果错误的具体值可分配给目标值，则错误与目标匹配
// 由目标指向，或者如果错误的方法为（接口{}）bool，则
// As（target）返回true。在后一种情况下，As方法负责
// 设定目标。
// None
// 错误类型可能提供As方法，因此可以将其视为
// 不同的错误类型。
// None
// 如果target不是指向实现的类型的非nil指针，则As将陷入恐慌
// 错误，或指向任何接口类型。
func As(err error, target interface{}) bool {
	if target == nil {
		panic("errors: target cannot be nil")
	}
	val := reflectlite.ValueOf(target)
	typ := val.Type()
	if typ.Kind() != reflectlite.Ptr || val.IsNil() {
		panic("errors: target must be a non-nil pointer")
	}
	targetType := typ.Elem()
	if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
		panic("errors: *target must be interface or implement error")
	}
	for err != nil {
		if reflectlite.TypeOf(err).AssignableTo(targetType) {
			val.Elem().Set(reflectlite.ValueOf(err))
			return true
		}
		if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
			return true
		}
		err = Unwrap(err)
	}
	return false
}

var errorType = reflectlite.TypeOf((*error)(nil)).Elem()
